package com.giteelou.simple;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 简单的脚本解释器。
 * 所支持的语法，请参见SimpleParser.java
 * 运行脚本：
 * * 在命令行下，键入：java SimpleScript
 * * 则进入一个REPL界面。你可以依次敲入命令。比如：
 * * > 2+3;
 * * > int age = 10;
 * * > int b;
 * * > b = 10*2;
 * * > age = age + b;
 * * > exit();  //退出REPL界面。
 * *
 * * 你还可以使用一个参数 -v，让每次执行脚本的时候，都输出AST和整个计算过程。
 */
public class SimpleScript {

    public static void main(String[] args) {
        SimpleScript script = new SimpleScript();
        if (args.length > 0 && "-v".equals(args[0])) {
            script.verbose = true;
        }

        SimpleParser parser = new SimpleParser();
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String scriptText = "";
        while (true) {
            System.out.print("\n>");
            try {
                String line = reader.readLine().trim();
                scriptText = scriptText + " " + line;
                if (!scriptText.endsWith(";")) {
                    continue;
                }
                if ("exit();".equals(scriptText.trim())) {
                    System.out.println("bye.");
                    break;
                }
                try {
                    ASTNode node = parser.parse(scriptText);
                    if (script.verbose) {
                        parser.dumpAST(node, "|-");
                    }
                    Integer result = script.evaluate(node);
                    System.out.println(result);
                } catch (Exception e) {
                    System.out.println(e.getLocalizedMessage());
                }
                scriptText = "";
            } catch (IOException e) {
                System.out.println(e.getLocalizedMessage());
                break;
            }
        }
    }

    /**
     * 记录变量的值
     */
    private final Map<String, Integer> variables;
    /**
     * 是否打印AST
     */
    private boolean verbose;

    public SimpleScript() {
        variables = new HashMap<>();
    }

    private Integer evaluate(ASTNode node) throws Exception {
        Integer val = null;
        switch (node.getType()) {
            case Program:
                // 计算所有节点，但只输出最后一个节点的值
                for (ASTNode child : node.getChildren()) {
                    val = evaluate(child);
                }
                break;
            case AdditiveExp:
                val = evaluate(node.getChildren().get(0))
                        + evaluate(node.getChildren().get(1));
                break;
            case MultiplicativeExp:
                val = evaluate(node.getChildren().get(0))
                        * evaluate(node.getChildren().get(1));
                break;
            case Identifier:
                if (!variables.containsKey(node.getText())) {
                    throw new Exception("unknown variable: " + node.getText());
                }
                val = variables.get(node.getText());
                if (val == null) {
                    throw new Exception("variable " + node.getText() + " not initialized");
                }
                break;
            case AssignmentStmt:
                if (!variables.containsKey(node.getText())) {
                    throw new Exception("unknown variable: " + node.getText());
                }
                // 不break;继续执行 case IntDeclaration:
            case IntDeclaration:
                List<ASTNode> children = node.getChildren();
                if (!children.isEmpty()) {
                    val = evaluate(children.get(0));
                    variables.put(node.getText(), val);
                } else {
                    variables.put(node.getText(), null);
                }
                break;
            case IntLiteral:
                val = Integer.valueOf(node.getText());
                break;
        }
        return val;
    }

}
